/*
 * pgp-armor.c
 *		PGP ASCII 装甲。
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation和/或其他提供的材料与分发一起。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, 或利润; 或业务中断)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * contrib/pgcrypto/pgp-armor.c
 */

#include "postgres.h"

#include "pgp.h"
#include "px.h"

/*
 * BASE64 - 重复 :(
 */

static const unsigned char _base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static int fc_pg_base64_encode(const uint8 *fc_src, unsigned fc_len, uint8 *fc_dst)
{
	uint8	   *fc_p,
			   *fc_lend = fc_dst + 76;
	const uint8 *fc_s,
			   *fc_end = fc_src + fc_len;
	int			fc_pos = 2;
	unsigned long fc_buf = 0;

	fc_s = fc_src;
	fc_p = fc_dst;

	while (fc_s < fc_end)
	{
		fc_buf |= *fc_s << (fc_pos << 3);
		fc_pos--;
		fc_s++;

		/*
		 * 写出它
		 */
		if (fc_pos < 0)
		{
			*fc_p++ = _base64[(fc_buf >> 18) & 0x3f];
			*fc_p++ = _base64[(fc_buf >> 12) & 0x3f];
			*fc_p++ = _base64[(fc_buf >> 6) & 0x3f];
			*fc_p++ = _base64[fc_buf & 0x3f];

			fc_pos = 2;
			fc_buf = 0;
		}
		if (fc_p >= fc_lend)
		{
			*fc_p++ = '\n';
			fc_lend = fc_p + 76;
		}
	}
	if (fc_pos != 2)
	{
		*fc_p++ = _base64[(fc_buf >> 18) & 0x3f];
		*fc_p++ = _base64[(fc_buf >> 12) & 0x3f];
		*fc_p++ = (fc_pos == 0) ? _base64[(fc_buf >> 6) & 0x3f] : '=';
		*fc_p++ = '=';
	}

	return fc_p - fc_dst;
}

/* 可能应该使用查找表 */
static int fc_pg_base64_decode(const uint8 *fc_src, unsigned fc_len, uint8 *fc_dst)
{
	const uint8 *fc_srcend = fc_src + fc_len,
			   *fc_s = fc_src;
	uint8	   *fc_p = fc_dst;
	char		fc_c;
	unsigned	fc_b = 0;
	unsigned long fc_buf = 0;
	int			fc_pos = 0,
				fc_end = 0;

	while (fc_s < fc_srcend)
	{
		fc_c = *fc_s++;
		if (fc_c >= 'A' && fc_c <= 'Z')
			fc_b = fc_c - 'A';
		else if (fc_c >= 'a' && fc_c <= 'z')
			fc_b = fc_c - 'a' + 26;
		else if (fc_c >= '0' && fc_c <= '9')
			fc_b = fc_c - '0' + 52;
		else if (fc_c == '+')
			fc_b = 62;
		else if (fc_c == '/')
			fc_b = 63;
		else if (fc_c == '=')
		{
			/*
			 * 结束序列
			 */
			if (!fc_end)
			{
				if (fc_pos == 2)
					fc_end = 1;
				else if (fc_pos == 3)
					fc_end = 2;
				else
					return PXE_PGP_CORRUPT_ARMOR;
			}
			fc_b = 0;
		}
		else if (fc_c == ' ' || fc_c == '\t' || fc_c == '\n' || fc_c == '\r')
			continue;
		else
			return PXE_PGP_CORRUPT_ARMOR;

		/*
		 * 添加到缓冲区
		 */
		fc_buf = (fc_buf << 6) + fc_b;
		fc_pos++;
		if (fc_pos == 4)
		{
			*fc_p++ = (fc_buf >> 16) & 255;
			if (fc_end == 0 || fc_end > 1)
				*fc_p++ = (fc_buf >> 8) & 255;
			if (fc_end == 0 || fc_end > 2)
				*fc_p++ = fc_buf & 255;
			fc_buf = 0;
			fc_pos = 0;
		}
	}

	if (fc_pos != 0)
		return PXE_PGP_CORRUPT_ARMOR;
	return fc_p - fc_dst;
}

static unsigned fc_pg_base64_enc_len(unsigned fc_srclen)
{
	/*
	 * 3 字节将转换为 4，76 个字符后换行
	 */
	return (fc_srclen + 2) * 4 / 3 + fc_srclen / (76 * 3 / 4);
}

static unsigned fc_pg_base64_dec_len(unsigned fc_srclen)
{
	return (fc_srclen * 3) >> 2;
}

/*
 * PGP 护甲
 */

static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n";
static const char *armor_footer = "\n-----END PGP MESSAGE-----\n";

/* 来自 rfc2440 的 CRC24 实现 */
#define CRC24_INIT 0x00b704ceL
#define CRC24_POLY 0x01864cfbL
static long fc_crc24(const uint8 *fc_data, unsigned fc_len)
{
	unsigned	fc_crc = CRC24_INIT;
	int			fc_i;

	while (fc_len--)
	{
		fc_crc ^= (*fc_data++) << 16;
		for (fc_i = 0; fc_i < 8; fc_i++)
		{
			fc_crc <<= 1;
			if (fc_crc & 0x1000000)
				fc_crc ^= CRC24_POLY;
		}
	}
	return fc_crc & 0xffffffL;
}

void pgp_armor_encode(const uint8 *fc_src, unsigned fc_len, StringInfo fc_dst,
				 int fc_num_headers, char **fc_keys, char **fc_values)
{
	int			fc_n;
	int			fc_res;
	unsigned	fc_b64len;
	unsigned	fc_crc = fc_crc24(fc_src, fc_len);

	appendStringInfoString(fc_dst, armor_header);

	for (fc_n = 0; fc_n < fc_num_headers; fc_n++)
		appendStringInfo(fc_dst, "%s: %s\n", fc_keys[fc_n], fc_values[fc_n]);
	appendStringInfoChar(fc_dst, '\n');

	/* 确保我们有足够的空间来 pg_base64_encode() */
	fc_b64len = fc_pg_base64_enc_len(fc_len);
	enlargeStringInfo(fc_dst, (int) fc_b64len);

	fc_res = fc_pg_base64_encode(fc_src, fc_len, (uint8 *) fc_dst->data + fc_dst->len);
	if (fc_res > fc_b64len)
		elog(FATAL, "overflow - encode estimate too small");
	fc_dst->len += fc_res;

	if (*(fc_dst->data + fc_dst->len - 1) != '\n')
		appendStringInfoChar(fc_dst, '\n');

	appendStringInfoChar(fc_dst, '=');
	appendStringInfoChar(fc_dst, _base64[(fc_crc >> 18) & 0x3f]);
	appendStringInfoChar(fc_dst, _base64[(fc_crc >> 12) & 0x3f]);
	appendStringInfoChar(fc_dst, _base64[(fc_crc >> 6) & 0x3f]);
	appendStringInfoChar(fc_dst, _base64[fc_crc & 0x3f]);

	appendStringInfoString(fc_dst, armor_footer);
}

static const uint8 *
fc_find_str(const uint8 *fc_data, const uint8 *fc_data_end, const char *fc_str, int fc_strlen)
{
	const uint8 *fc_p = fc_data;

	if (!fc_strlen)
		return NULL;
	if (fc_data_end - fc_data < fc_strlen)
		return NULL;
	while (fc_p < fc_data_end)
	{
		fc_p = memchr(fc_p, fc_str[0], fc_data_end - fc_p);
		if (fc_p == NULL)
			return NULL;
		if (fc_p + fc_strlen > fc_data_end)
			return NULL;
		if (memcmp(fc_p, fc_str, fc_strlen) == 0)
			return fc_p;
		fc_p++;
	}
	return NULL;
}

static int fc_find_header(const uint8 *fc_data, const uint8 *fc_datend,
			const uint8 **fc_start_p, int fc_is_end)
{
	const uint8 *fc_p = fc_data;
	static const char *fc_start_sep = "-----BEGIN";
	static const char *fc_end_sep = "-----END";
	const char *fc_sep = fc_is_end ? fc_end_sep : fc_start_sep;

	/* 查找头行 */
	while (1)
	{
		fc_p = fc_find_str(fc_p, fc_datend, fc_sep, strlen(fc_sep));
		if (fc_p == NULL)
			return PXE_PGP_CORRUPT_ARMOR;
		/* 它必须从行首开始 */
		if (fc_p == fc_data || *(fc_p - 1) == '\n')
			break;
		fc_p += strlen(fc_sep);
	}
	*fc_start_p = fc_p;
	fc_p += strlen(fc_sep);

	/* 检查头文本是否正常 */
	for (; fc_p < fc_datend && *fc_p != '-'; fc_p++)
	{
		/* 可能会有各种杂物，但绝对不能有换行符 */
		if (*fc_p >= ' ')
			continue;
		return PXE_PGP_CORRUPT_ARMOR;
	}
	if (fc_datend - fc_p < 5 || memcmp(fc_p, fc_sep, 5) != 0)
		return PXE_PGP_CORRUPT_ARMOR;
	fc_p += 5;

	/* 检查是否在行末 */
	if (fc_p < fc_datend)
	{
		if (*fc_p != '\n' && *fc_p != '\r')
			return PXE_PGP_CORRUPT_ARMOR;
		if (*fc_p == '\r')
			fc_p++;
		if (fc_p < fc_datend && *fc_p == '\n')
			fc_p++;
	}
	return fc_p - *fc_start_p;
}

int pgp_armor_decode(const uint8 *fc_src, int fc_len, StringInfo fc_dst)
{
	const uint8 *fc_p = fc_src;
	const uint8 *fc_data_end = fc_src + fc_len;
	long		fc_crc;
	const uint8 *fc_base64_start,
			   *fc_armor_end;
	const uint8 *fc_base64_end = NULL;
	uint8		fc_buf[4];
	int			fc_hlen;
	int			fc_blen;
	int			fc_res = PXE_PGP_CORRUPT_ARMOR;

	/* 护甲开始 */
	fc_hlen = fc_find_header(fc_src, fc_data_end, &fc_p, 0);
	if (fc_hlen <= 0)
		goto out;
	fc_p += fc_hlen;

	/* 护甲结束 */
	fc_hlen = fc_find_header(fc_p, fc_data_end, &fc_armor_end, 1);
	if (fc_hlen <= 0)
		goto out;

	/* 跳过注释 - 查找空行 */
	while (fc_p < fc_armor_end && *fc_p != '\n' && *fc_p != '\r')
	{
		fc_p = memchr(fc_p, '\n', fc_armor_end - fc_p);
		if (!fc_p)
			goto out;

		/* 步进到下一行的开始 */
		fc_p++;
	}
	fc_base64_start = fc_p;

	/* 查找 crc 位置 */
	for (fc_p = fc_armor_end; fc_p >= fc_base64_start; fc_p--)
		if (*fc_p == '=')
		{
			fc_base64_end = fc_p - 1;
			break;
		}
	if (fc_base64_end == NULL)
		goto out;

	/* 解码 crc */
	if (fc_pg_base64_decode(fc_p + 1, 4, fc_buf) != 3)
		goto out;
	fc_crc = (((long) fc_buf[0]) << 16) + (((long) fc_buf[1]) << 8) + (long) fc_buf[2];

	/* 解码数据 */
	fc_blen = (int) fc_pg_base64_dec_len(fc_len);
	enlargeStringInfo(fc_dst, fc_blen);
	fc_res = fc_pg_base64_decode(fc_base64_start, fc_base64_end - fc_base64_start, (uint8 *) fc_dst->data);
	if (fc_res > fc_blen)
		elog(FATAL, "overflow - decode estimate too small");
	if (fc_res >= 0)
	{
		if (fc_crc24((uint8 *) fc_dst->data, fc_res) == fc_crc)
			fc_dst->len += fc_res;
		else
			fc_res = PXE_PGP_CORRUPT_ARMOR;
	}
out:
	return fc_res;
}

/*
 * 从 ASCII 护甲输入中提取所有护甲头。
 *
 * 成功时返回 0，出错时返回 PXE_* 错误代码。成功时，
 * 头的数量及其键和值将返回在 *nheaders,
 * *nkeys 和 *nvalues 中。
 */
int pgp_extract_armor_headers(const uint8 *fc_src, unsigned fc_len,
						  int *fc_nheaders, char ***fc_keys, char ***fc_values)
{
	const uint8 *fc_data_end = fc_src + fc_len;
	const uint8 *fc_p;
	const uint8 *fc_base64_start;
	const uint8 *fc_armor_start;
	const uint8 *fc_armor_end;
	Size		fc_armor_len;
	char	   *fc_line;
	char	   *fc_nextline;
	char	   *fc_eol,
			   *fc_colon;
	int			fc_hlen;
	char	   *fc_buf;
	int			fc_hdrlines;
	int			fc_n;

	/* 护甲开始 */
	fc_hlen = fc_find_header(fc_src, fc_data_end, &fc_armor_start, 0);
	if (fc_hlen <= 0)
		return PXE_PGP_CORRUPT_ARMOR;
	fc_armor_start += fc_hlen;

	/* 护甲结束 */
	fc_hlen = fc_find_header(fc_armor_start, fc_data_end, &fc_armor_end, 1);
	if (fc_hlen <= 0)
		return PXE_PGP_CORRUPT_ARMOR;

	/* 计算护甲头行的数量。 */
	fc_hdrlines = 0;
	fc_p = fc_armor_start;
	while (fc_p < fc_armor_end && *fc_p != '\n' && *fc_p != '\r')
	{
		fc_p = memchr(fc_p, '\n', fc_armor_end - fc_p);
		if (!fc_p)
			return PXE_PGP_CORRUPT_ARMOR;

		/* 步进到下一行的开始 */
		fc_p++;
		fc_hdrlines++;
	}
	fc_base64_start = fc_p;

	/*
	 * 制作一个可修改的输入部分的副本，该部分包含
	 * 头。返回的键/值指针将指向缓冲区内部。
	 */
	fc_armor_len = fc_base64_start - fc_armor_start;
	fc_buf = palloc(fc_armor_len + 1);
	memcpy(fc_buf, fc_armor_start, fc_armor_len);
	fc_buf[fc_armor_len] = '\0';

	/* 分配返回数组 */
	*fc_keys = (char **) palloc(fc_hdrlines * sizeof(char *));
	*fc_values = (char **) palloc(fc_hdrlines * sizeof(char *));

	/*
	 * 在换行符和 ": " 分隔符处分割头行，并在返回数组中收集
	 * 键和值的指针。
	 */
	fc_n = 0;
	fc_line = fc_buf;
	for (;;)
	{
		/* 查找行尾 */
		fc_eol = strchr(fc_line, '\n');
		if (!fc_eol)
			break;
		fc_nextline = fc_eol + 1;
		/* 如果该行以 CR + LF 结尾，则去掉 CR */
		if (fc_eol > fc_line && *(fc_eol - 1) == '\r')
			fc_eol--;
		*fc_eol = '\0';

		/* 查找分隔键和值的冒号+空格 */
		fc_colon = strstr(fc_line, ": ");
		if (!fc_colon)
			return PXE_PGP_CORRUPT_ARMOR;
		*fc_colon = '\0';

		/* 不应该发生，我们提前计算了行数 */
		if (fc_n >= fc_hdrlines)
			elog(ERROR, "unexpected number of armor header lines");

		(*fc_keys)[fc_n] = fc_line;
		(*fc_values)[fc_n] = fc_colon + 2;
		fc_n++;

		/* 步进到下一行的开始 */
		fc_line = fc_nextline;
	}

	if (fc_n != fc_hdrlines)
		elog(ERROR, "unexpected number of armor header lines");

	*fc_nheaders = fc_n;
	return 0;
}
